using System;
using Microsoft.SPOT;
using System.IO;
using GHIElectronics.NETMF.USBHost;
using GHIElectronics.NETMF.System;
using PTP.Devices;
using System.Collections;
using PTP.Core;

namespace PTP.Usb.GHI
{
  /// <summary>
  /// Handles all USB connections of PTP devices
  /// </summary>
  public class GHIConnectionManager : IConnectionManager
  {
    private static GHIConnectionManager _instance = null;

    public static GHIConnectionManager Instance
    {
      get
      {
        if (_instance == null)
          _instance = new GHIConnectionManager();
        return _instance;
      }
    }

    public GHIConnectionManager()
    {
      USBHostController.DeviceConnectedEvent += new USBH_DeviceConnectionEventHandler(USBHostController_DeviceConnectedEvent);
    }

    void USBHostController_DeviceConnectedEvent(USBH_Device device)
    {
      if (DeviceConnected != null)
      {
        IPTPDevice dev = readDeviceInfo(device);
        if (dev != null)
          DeviceConnected(dev);
      }
    }

    private static bool isPTPInterface(USBH_Descriptors.Interface inter)
    {
      return InterfaceInfo.IsPTPInterface(inter.bInterfaceClass, inter.bInterfaceSubclass, inter.bInterfaceProtocol);
    }

    public bool IsPTPDevice(USBH_RawDevice device)
    {
		USBH_Descriptors.Device d = device.GetDeviceDescriptor();
      for (byte i = 0; i < d.bNumConfigurations; i++)
      {
		  USBH_Descriptors.Configuration c = device.GetConfigurationDescriptors(i);
        for (int n = 0; n < c.interfaces.Length; n++)
        {
			USBH_Descriptors.Interface inter = c.interfaces[n];
          // Check if we can handle this interface
          if (!isPTPInterface(inter))
            continue;
          return true;
        }
      }
      return false;
    }

    public Stream OpenStream(USBH_RawDevice device)
    {
      USBH_RawDevice.Pipe inPipe = null;
      USBH_RawDevice.Pipe outPipe = null;
	  USBH_Descriptors.Device d = device.GetDeviceDescriptor();
      for (byte i = 0; i < d.bNumConfigurations; i++)
      {
		  USBH_Descriptors.Configuration c = device.GetConfigurationDescriptors(i);

        for (int n = 0; n < c.interfaces.Length; n++)
        {
          //TODO: this could open many pipes if device has many PTP interfaces
			USBH_Descriptors.Interface inter = c.interfaces[n];
          // Check if we can handle this interface
          if (!isPTPInterface(inter))
            continue;

          for (int e = 0; e < inter.endpoints.Length; e++)
          {
			  USBH_Descriptors.Endpoint endP = inter.endpoints[e];

            if ((endP.bDescriptorType == 5) && (endP.bEndpointAddress >= 0x80))
            {

              if ((endP.bmAttributes == 2)) // IN
              {
                inPipe = device.OpenPipe(endP);
                inPipe.TransferTimeout = 0;
              }

              //TODO: interrupt pipe?
            }

            if ((endP.bDescriptorType == 5) && (endP.bEndpointAddress < 0x80) && (endP.bmAttributes == 2))
            {
              outPipe = device.OpenPipe(endP);
            }
          }
        }

        if (inPipe == null)
          throw new Exception("No in pipe");
        if (outPipe == null)
          throw new Exception("No out pipe");

        Stream result = new USBH_PipeStream(inPipe, outPipe);

        //GetStandardDeviceStatus();

        //TODO: Check what this line is for
        //SETUP???
        device.SendSetupTransfer(0x00, 0x09, c.bConfigurationValue, 0x00);

        return result;
      }
      throw new Exception("Not a PTP device");
    }

    private IPTPDevice readDeviceInfo(USBH_Device device)
    {
      //check if this is PTP device
      USBH_RawDevice rd = new USBH_RawDevice(device);
      GHIConnectionManager cm = GHIConnectionManager.Instance;
      if (!cm.IsPTPDevice(rd))
        return null;

      //initialize device
      Stream s = cm.OpenStream(rd);
      return DeviceFactory.Instance.CreateDevice(s);
    }

    #region IConnectionManager Members

    public event PTPDeviceEvent DeviceConnected;

    public event PTPDeviceEvent DeviceDisconnected;

    public IPTPDevice[] GetCurrentDevices()
    {
      USBH_Device[] deviceList = USBHostController.GetDevices();
      ArrayList list = new ArrayList();
      //loop through connected devices
      for (int i = 0; i < deviceList.Length; i++)
      {
        USBH_Device device = deviceList[i];
        IPTPDevice dev = this.readDeviceInfo(device);
        if (dev != null)
          list.Add(dev);
      }
      return (IPTPDevice[])list.ToArray(typeof(IPTPDevice));
    }

    #endregion
  }
}